Using Suspense in Next
As we saw in the Sole&Ankle example, the primary way to access Suspense in Next is through loading.js
.
When we create a file named loading.js
and colocate it next to page.js
, Next will automatically wrap the two in a Suspense boundary. Somewhere in the Next.js codebase, there's likely some code that looks like this:
function NextApp({ params, LoadingComponent, PageComponent }) { if (LoadingComponent) { return ( <React.Suspense fallback={<LoadingComponent params={params} />} > <PageComponent params={params} /> </React.Suspense> ) }
return ( <PageComponent params={params} /> );}
By creating a loading.js
component, we opt in to Suspense.
Hang on a second, though… Earlier, I mentioned that there were two parts of the Suspense API:
- Creating a Suspense boundary with
<React.Suspense>
. - Broadcasting the loading status for components within the boundary.
Creating the boundary is only half of the story. We also need a way for the components within that boundary to say “Hey, don't render yet! I'm still waiting on data”.
How do we do that in Next.js? Do we need to throw a promise somewhere??
Fortunately not. React Server Components is here to save the day.
Let's take another look at the page.js
component from that project:
// /src/app/shop/[categorySlug]/page.jsimport React from 'react';
import { getShoesForCategory } from '@/helpers/data';import ShoeGrid from '@/components/ShoeGrid';
async function CategoryPage({ params }) { const { categorySlug } = await params; const shoes = await getShoesForCategory(categorySlug);
return <ShoeGrid shoes={shoes} />;}
export default CategoryPage;
This component is a Server Component, since it doesn't include the "use client"
directive. This means that it only ever renders on the server.
In a lot of ways, Server Components are simpler than Client Components. They never re-render; after their first render, they're done.
Server Components are also allowed to be asynchronous. Notice that we're using the async
keyword here, in the function definition. This means that when we render this component, we get back a Promise, and that promise will resolve after the data-fetching is complete and the component has been rendered.
With Server Components, React can use the component itself to track whether the component is ready or not. 🤯
I recognize, this stuff is really complicated, and I'm probably losing a lot of you. But here's the important takeaway: when we use React Server Components with Suspense, things “Just Work™”. We don't have to use any special libraries, we don't have to throw
anything. When we put an async Server Component within a Suspense boundary, React will suspend until it's finished rendering.
It's really pretty remarkable to me how well all of these modern pieces fit together. It's no accident; the React team undoubtedly worked very hard to come up with a cohesive modern suite of APIs. But my goodness, it's a thing of beauty.